import tkinter as tk
from tkinter import Canvas, Label
from pynput import mouse, keyboard
import threading
import numpy as np
from PIL import ImageGrab
import time
import os
import sys
import logging

# 解决Windows下ImageGrab截图权限问题（关键修复）
if sys.platform == 'win32':
    try:
        from ctypes import windll
        windll.user32.SetProcessDPIAware()  # 适配高DPI，避免截图偏移
    except:
        pass

class ScreenMonitor:
    def __init__(self):
        # 核心参数
        self.monitor_area = None
        self.drawing = False
        self.threshold = 3000
        self.running = True
        self.monitor_thread = None
        self.cmd_minimized = False

        # 日志配置（详细且直观，方便排查）
        self.setup_logger()

        # 强制流程：1. 最小化CMD → 2. 弹框选 → 3. 框选后监控
        self.minimize_cmd()  # 第一步：优先最小化CMD（修复窗口查找bug）
        self.show_selection_window()  # 第二步：弹框选（增加操作提示）
        self.start_exit_hotkey()  # 第三步：启动退出快捷键

    def setup_logger(self):
        """日志同时写文件和CMD（最小化前能看到，方便调试）"""
        log_file = "screen_monitor.log"
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(levelname)s - %(message)s',
            handlers=[
                logging.FileHandler(log_file, encoding='utf-8'),
                logging.StreamHandler(sys.stdout)
            ]
        )
        self.logger = logging.getLogger(__name__)

    def minimize_cmd(self):
        """修复Windows CMD最小化bug：直接获取控制台窗口句柄（比枚举更可靠）"""
        self.logger.info("开始最小化CMD窗口...")
        if sys.platform != 'win32':
            self.logger.warning("非Windows系统，跳过自动最小化，请手动最小化终端")
            return

        try:
            import win32gui
            import win32con
            import win32console  # 直接操作控制台窗口（关键修复）

            # 获取当前CMD窗口句柄（避免枚举窗口找不到的问题）
            console_handle = win32console.GetConsoleWindow()
            if console_handle:
                # 最小化窗口到任务栏
                win32gui.ShowWindow(console_handle, win32con.SW_MINIMIZE)
                self.cmd_minimized = True
                self.logger.info("✅ CMD窗口已成功最小化到任务栏")
            else:
                self.logger.error("❌ 未找到CMD窗口，无法自动最小化")
                print("未找到CMD窗口，请手动最小化")
                time.sleep(1)

        except ImportError:
            self.logger.error("❌ 未安装pywin32，无法自动最小化CMD")
            print("请安装pywin32：pip install pywin32，或手动最小化CMD窗口")
            time.sleep(2)
        except Exception as e:
            self.logger.error(f"❌ CMD最小化失败：{str(e)}")
            print(f"CMD最小化失败，请手动最小化：{str(e)}")
            time.sleep(2)

    def show_selection_window(self):
        """修复框选窗口：增加操作提示，确保可见"""
        self.logger.info("弹出框选窗口，请拖动鼠标选择监控区域...")
        self.root = tk.Tk()
        self.root.title("选择监控区域")
        self.root.attributes('-alpha', 0.5)  # 半透明，兼顾可见性和不遮挡
        self.root.attributes('-topmost', True)  # 强制置顶，必见
        self.root.overrideredirect(True)  # 无边框

        # 全屏覆盖
        screen_w = self.root.winfo_screenwidth()
        screen_h = self.root.winfo_screenheight()
        self.root.geometry(f"{screen_w}x{screen_h}+0+0")

        # 画布（框选区域）
        self.canvas = Canvas(self.root, bg='white', highlightthickness=0)
        self.canvas.pack(fill=tk.BOTH, expand=True)
        self.rect_id = None

        # 增加操作提示文字（避免用户不知道该做什么）
        Label(
            self.canvas,
            text="拖动鼠标框选监控区域（松开鼠标开始监控）",
            bg='red', fg='white', font=('Arial', 14, 'bold')
        ).place(x=screen_w//2 - 200, y=30)

        # 绑定鼠标事件（修复事件绑定失效问题）
        self.canvas.bind('<ButtonPress-1>', self.on_mouse_down)
        self.canvas.bind('<B1-Motion>', self.on_mouse_drag)
        self.canvas.bind('<ButtonRelease-1>', self.on_mouse_up)

    def start_exit_hotkey(self):
        """启动Ctrl+Q退出快捷键（确保后台可用）"""
        try:
            self.keyboard_listener = keyboard.GlobalHotKeys({
                '<ctrl>+q': self.stop_program
            })
            self.keyboard_listener.start()
            self.logger.info("✅ 退出快捷键已启动：Ctrl+Q")
        except Exception as e:
            self.logger.error(f"❌ 快捷键启动失败：{str(e)}")

    def on_mouse_down(self, event):
        """鼠标按下：开始框选"""
        self.start_x, self.start_y = event.x, event.y
        self.drawing = True
        self.logger.info(f"框选开始：({self.start_x}, {self.start_y})")

    def on_mouse_drag(self, event):
        """鼠标拖动：实时画框（修复矩形刷新bug）"""
        if not self.drawing:
            return
        current_x, current_y = event.x, event.y
        left = min(self.start_x, current_x)
        top = min(self.start_y, current_y)
        right = max(self.start_x, current_x)
        bottom = max(self.start_y, current_y)

        # 修复矩形刷新：先删后画，避免残留
        if self.rect_id:
            self.canvas.delete(self.rect_id)
        self.rect_id = self.canvas.create_rectangle(
            left, top, right, bottom,
            outline='red', width=4, dash=(3, 2)  # 加粗红色虚线，更醒目
        )

    def on_mouse_up(self, event):
        """鼠标松开：确认区域，启动监控（修复监控线程不启动bug）"""
        self.drawing = False
        self.end_x, self.end_y = event.x, event.y

        # 计算区域（确保坐标正确）
        left = min(self.start_x, self.end_x)
        top = min(self.start_y, self.end_y)
        right = max(self.start_x, self.end_x)
        bottom = max(self.start_y, self.end_y)
        width = right - left
        height = bottom - top

        # 验证区域有效性
        if width < 20 or height < 20:  # 放宽最小尺寸，避免误判
            self.logger.error(f"❌ 框选区域过小（当前{width}x{height}），请重新选择（至少20x20像素）")
            if self.rect_id:
                self.canvas.delete(self.rect_id)
            return

        # 保存区域，关闭框选窗口
        self.monitor_area = (left, top, right, bottom)
        self.root.destroy()
        self.logger.info(f"✅ 框选完成：区域 ({left}, {top})-({right}, {bottom})，尺寸 {width}x{height}")
        self.logger.info("启动后台监控...")

        # 修复监控线程：确保线程启动（非守护线程）
        self.monitor_thread = threading.Thread(target=self.start_monitoring)
        self.monitor_thread.daemon = False  # 关键：主线程退出后监控线程仍运行
        self.monitor_thread.start()
        self.logger.info("✅ 监控线程已启动，开始检测屏幕波动")

    def start_monitoring(self):
        """核心监控逻辑（修复截图和差异计算bug）"""
        last_frame = None
        frame_count = 0

        while self.running and self.monitor_area:
            try:
                # 修复截图bug：确保捕获正确区域，适配高DPI
                current_frame = ImageGrab.grab(bbox=self.monitor_area)
                current_frame_np = np.array(current_frame)

                # 日志心跳（证明监控在运行）
                frame_count += 1
                if frame_count % 20 == 0:
                    self.logger.info(f"🔍 监控正常：已运行 {frame_count//2} 秒，捕获 {frame_count} 帧")

                # 计算帧差异（简化逻辑，避免计算错误）
                if last_frame is not None:
                    # 计算灰度图差异（比RGB更高效，减少误报）
                    current_gray = np.mean(current_frame_np, axis=2).astype(np.uint8)
                    last_gray = np.mean(last_frame, axis=2).astype(np.uint8)
                    diff_sum = np.sum(np.abs(current_gray - last_gray))

                    # 触发警报
                    if diff_sum > self.threshold:
                        self.logger.warning(f"🚨 检测到屏幕波动！差异值：{diff_sum:.0f}（阈值：{self.threshold}）")
                        self.play_alert()
                        time.sleep(2)  # 冷却避免重复警报

                # 更新上一帧（深拷贝，避免引用问题）
                last_frame = current_frame_np.copy()
                time.sleep(0.5)

            except Exception as e:
                # 详细日志，方便排查监控失败原因
                self.logger.error(f"❌ 监控异常：{str(e)}", exc_info=True)
                time.sleep(1)  # 出错后重试，避免程序崩溃

    def play_alert(self):
        """播放警报（修复音效失效，增加备选方案）"""
        try:
            if os.path.exists('alert.mp3'):
                from playsound import playsound
                playsound('alert.mp3')
            else:
                self.logger.warning("⚠️  未找到alert.mp3，播放系统蜂鸣音")
                # 跨平台蜂鸣（确保能发出提示）
                if sys.platform == 'win32':
                    import winsound
                    winsound.Beep(1500, 800)  # 高频长蜂鸣，更醒目
                else:
                    os.system('echo -e "\a" && sleep 0.2 && echo -e "\a"')  # 两声蜂鸣
        except Exception as e:
            self.logger.error(f"❌ 警报播放失败：{str(e)}")

    def stop_program(self):
        """优雅退出（确保所有资源释放）"""
        self.logger.info("🛑 收到退出指令，正在关闭程序...")
        self.running = False

        # 停止快捷键监听
        if hasattr(self, 'keyboard_listener') and self.keyboard_listener.is_alive():
            self.keyboard_listener.stop()

        # 等待监控线程结束
        if self.monitor_thread and self.monitor_thread.is_alive():
            self.monitor_thread.join(timeout=2)

        self.logger.info("✅ 程序已完全退出")
        sys.exit(0)

if __name__ == "__main__":
    try:
        # 启动程序（确保流程正确）
        monitor = ScreenMonitor()
        monitor.root.mainloop()  # 保持框选窗口运行
    except KeyboardInterrupt:
        if 'monitor' in locals():
            monitor.stop_program()
    except Exception as e:
        logging.error(f"❌ 程序启动失败：{str(e)}", exc_info=True)
        sys.exit(1)